跳到主要内容

TCP 的可靠传输

TCP 的可靠传输

注意:TCP 协议是不分什么客户端服务端的,这里为了方便理解把对端称之为服务端

TCP 在 IP 的不可靠的尽最大努力服务的基础上实现了一种可靠的数据传输服务,保证数据无差错、无丢失、按序和无重复的交付。差错检测、序号、确认、超时重传、滑动窗口等,这里先简单介绍一下这个概念,具体细节看下面几节

在连接状态建立后就需要开始传输数据了,这时就需要通过一系列的机制从而保证可靠的连接

首先一包数据有可能会被拆成多包发送,如何处理丢包问题,这些数据包到达的先后顺序也不同,所以如何处理乱序的问题,基于这些问题 TCP 协议为每一个连接建立了一个发送缓冲区,从建立链接后的第一个字节的序号为 0,后面每一个字节的序列号就会增加 1

发送数据时,取一部分数据组成发送报文,在其 TCP 协议头中会附带序列号和长度

接收端在收到数据后,需要回复确认报文,确认报文中的 ACK 等于接收序列号和长度(也就是下一包数据需要发送的起始序列号)

发送端也可以连续发多包数据,接收端只需回复一次 ACK 就行了,接收方收到这些数据后,根据序列号和长度重组,当丢失的部分会要求重传

例如接收端向发送端发送 ACK 等于 100 的报文,发送端会重传 100 之后的数据包,接收端再进行补齐

注意:以上过程不分客户端服务端,TCP 是全双工的,即它们既可能是客户端有可能是服务端

数据编号与确认

TCP 协议是面向字节的。TCP 把应用层交下来的长报文(这可能要划分为许多较短的报文段)看成是一个个字节组成的数据流,并使每一个字节对应于一个序号。注意,在 GBN 协议中是对每个分组进行编号的。在连接建立时,双方 TCP 要各自确定初始序号。TCP 每次发送的报文段的首部中的序号字段数值表示该报文段中紧接着首部后面的第一个数据字节的序号。

TCP 使用的是累积确认,即确认是对所有按序接收到的数据的确认。但请注意,接收方返回的确认号是已按序收到的数据的最高序号加 1。也就是说,确认号表示接收方期望下次收到的数据中的第一个数据字节序号。

TCP 的报文到达确认(ACK),是对接收到的数据的最高序列号的确认,并向发送端返回一个下次接收时期望的 TCP 数据包的序列号(Ack Number)。(只有当 ACK=1 时确认号字段才有效。当 ACK=0时,确认号无效)

而发的每一个包对端都需要 ACK 确认(也有采用了延时策略)

例如:已经收到了1~700号、801~1000号和1201~1500号,而701~800号及1001~1200号的数据还没有收到,那么这时发送的确认序号应填入701。

当 TCP 发送一报文段时,它同时也在自己的重传队列中存放这个报文段的一个副本。若收到确认,则删除此副本。若在规定时间内没有收到确认,则重传此报文段的副本。TCP 的确认并不保证数据已交付给了应用进程,而只是表明在接收方的 TCP 已按序正确收到了对方所发送的报文段。

由于 TCP 连接能提供全双工通信,因此通信中的每一方都不必专门发送确认报文段,而可以在传送数据时顺便把确认信息捎带传送(确认号)。为此,TCP 采用了一种延迟确认的机制,即接收方在正确接收到数据时可能要等待一小段时间(一般不超过 0.5s)再发送确认。若这段时间内有数据要发送给对方,则可以捎带确认。也有可能在这段时间内又有数据到达,则可以同时对这两次到达的数据进行累积确认。这样做可以减少发送完全不带数据的确认报文段,以提高 TCP的传输效率。接收方若收到有差错的报文段就丢弃(不发送否认信息)。若收到重复的报文段,也要丢弃,但要立即发回确认信息。这一点是非常重要的。

若收到的报文段无差错,只是未按序号顺序到达,那么应如何处理?在 GBN 中会丢弃所有未按序到达的分组,但是 TCP 对此未做明确规定,而是让 TCP 的实现者自行确定。可以像GBN一样将不按序的报文段丢弃,但多数TCP实现是先将其暂存于接收缓存内,待所缺序号的报文段收齐后再一起上交应用层。在互联网环境中,封装 TCP 报文段的 IP 数据报不一定是按序到达的,将失序的报文段先缓存起来可以避免不必要的重传。注意,不论采用哪种方法,接收方都要立即对已按序接收到的数据进行确认。ACK

TCP 发送方每发送一个报文段,就会为这个报文段设置一个计时器。只要计时器设置的重传时间已经到了但还没有收到确认,就要重传这一报文段。我们知道,在 GBN 中,一旦发送方某个分组超时,则会重传发送窗口内所有已发送的分组。而在 TCP 中发送方只会重传超时的那一个报文段,如果后续报文段的确认能够在超时之前及时到达,则不会重传那些还没有超时的后续报文段。

滑动窗口

为了提高报文段的传输效率,TCP采用滑动窗口协议。但与 GBN 不同的是,TCP 发送窗口大小的单位是字节,而不是分组数。TCP 发送方

已发送的未被确认的字节数不能超过发送窗口的大小。

  • 落入发送窗口内的是允许发送的字节
  • 落在发送窗口外左侧的是已发送并被确认的字节
  • 落在发送窗口外右侧的是还不能发送的字节。

收到确认后,发送窗口向右滑动,直到发送窗口的左沿正好包含确认序号的字节。

image.png

具体的两方是如何发送接收数据的

发送方的应用进程把字节流写入 TCP的发送缓存,接收方的应用进程从TCP的接收缓存中读取字节流。

image.png

发送方的情况(如图5-10(a)所示)

发送缓存用来暂时存放:

  1. 发送应用程序传送给发送方 TCP 准备发送的数据;
  2. TCP 已发送出去但尚未收到确认的数据。

发送窗口通常只是发送缓存的一部分。已被确认的数据应当从发送缓存中删除,因此发送缓存和发送窗口的后沿是重合的。发送应用程序最后写入发送缓存的字节减去最后被确认的字节,就是还保留在发送缓存的被写入的字节数。

如果发送应用程序传送给 TCP 发送方的速度太快,可能会最终导致发送缓存被填满,这时发送应用程序必须等待,直到有数据从发送缓存中删除。

接收方的情况(如图5-10(b)所示)

接收缓存用来暂时存放:

  1. 按序到达的,但尚未被接收应用程序读取的数据;
  2. 未按序到达的,但还不能被接收应用程序读取的数据。

如果收到的分组被检测出有差错,则要丢弃。如果接收应用程序来不及读取收到的数据,接收缓存最终就会被填满,使接收窗口减小到零。反之,如果接收应用程序能够及时从接收缓存中读取收到的数据,接收窗口就会增大,但最大不能超过接收缓存的大小。

图5-10(b)中还指出了下一个期望收到的字节号。这个字节号也就是接收方给发送方的报文段的首部中的确认号。

快速重传

image.png

假定发送方发送了报文段 M1~M5 共5 个报文段。接收方每收到一个报文段后都要立即发出确认,而不要等待自己发送数据时才进行捎带确认。当接收方收到了 M1 后,发出对 M1 的确认。假定由于网络拥塞使 M2 丢失了。接收方后来收到下一个 M3,发现其序号不对,但仍收下放在缓存中,同时发出对最近按序接收的 M1 确认(请注意,不能对 M3 确认,因为 TCP 是累积确认,如果对 M3确认就表示 M2也已经收到了)。因为 TCP 不使用否定确认,当接收方收到失序报文段时,不能向发送方发回一个显式的否定确认,而只需对按序接收到的最后一个字节数据进行重复确认。当接收方收到 M4 和 M5 后,也还要分别发出对 M1 的重复确认。这样,当发送方收到一连三个重复的确认后,就知道现在可能是网络出现了拥塞造成分组丢失,或是报文段 M2 虽未丢失但目前正滞留在网络中的某处,可能还要经过较长的时延才能到达接收方。快速重传算法规定,发送方只要一连收到三个重复的确认,就应立即重传丢失的报文段 M2(注意:重复确认的确认号正是要重传的报文段的序号),而不必继续等待为M2设置的重传计时器的超时。不难看出,快速重传并非取消重传计时器,而是尽早重传丢失的报文段。

选择确认

按照 TCP 协议,确认机制是累积的,也就是确认号 X 的确认指示的是所有 X 之前但不包括 X 的数据已经收到了(确认号本身就是不含数据的分段)。而没有通告所有收到的失序到达的那些字节,虽然这些字节已经被接收方接收并暂存在接收缓存中。但是这些没有被确认的字节很可能因为超时而被发送方重传。为避免这些无意义的重传,一个可选的功能选择确认(Selective ACK,SACK)可以用来解决这个问题。选择确认允许接收方通知发送方所有正确接收了的但是失序的字节块,发送方可以根据这些信息只重传那些接收方还没有收到的字节块。

image.png

接收方要将这些接收到的失序字节块通告给对方,只使用一个确认号是办不到的。从图5-14可以看出,每一个字节块需要用两个边界序号来表示。

例如,第一个失序的字节块的左边界 L1 为1501,右边界 R1 为2001。这里有两个失序字节块,因此需要4个边界序号来表示。但我们知道,TCP 的固定首部中没有哪个字段能提供上述这些字节块的边界信息,因此 TCP 在首部中提供了一个可变长的 “SACK选项字段” 来存放这些信息。除此之外,要使用选择确认功能,在建立 TCP 连接时,双方还要分别在 SYN 报文段和 SYN+ACK 报文段的首部选项中都添加 “允许 SACK 选项字段”,表示都支持选择确认功能。之后,才能在数据传输阶段使用 SACK 选项字段进行选择确认。当使用选择确认时,TCP 首部中的 “确认号字段” 的功能和意义并没有改变,实际上 SACK 是对原来累积确认功能的一种补充,并可以和使用累积确认的超时重传与快速重传机制一起工作。目前多数 TCP 实现都支持选择确认功能。

超时重传时间

TCP 的发送方在规定的时间内没有收到确认就要重传已发送的报文段。这种重传的概念很简单,但如何选择超时重传的时间却是 TCP 中非常重要也是较复杂的一个问题。

这个以后用到补充~

Reference

一条视频讲清楚TCP协议与UDP协议-什么是三次握手与四次挥手